﻿Imports System.Reflection
Imports System.Linq.Expressions

''' <summary>
''' リテラルプロパティ
''' </summary>
''' <remarks></remarks>
Public Class LiteralProperty
    Inherits PropertyInfo

    Public Shared Event OnLiteralNameBuiding As EventHandler(Of LiteralNameBuildingEventArgs)

    Public Shared Function Create(prop As PropertyInfo) As LiteralProperty
        If prop Is Nothing Then Throw New ArgumentNullException("prop")

        Dim c As New LiteralProperty
        c.PropertyInfo = prop
        c.SystemName = prop.Name

        'リテラル名キャッシュ
        Dim e As New LiteralNameBuildingEventArgs With {.PropertyInfo = prop, .LiteralName = prop.Name}
        c.RaiseLiteralNameBuildingEvent(e)
        c.LiteralName = e.LiteralName

        'システム属性キャッシュ
        c.SystemValueAttribute = DirectCast(c.PropertyInfo.GetCustomAttributes(GetType(SpecialValueAttribute), False).FirstOrDefault, SpecialValueAttribute)

        'デリゲートコンパイル
        c.Getter = LiteralProperty.CreateGetDelegate(prop.DeclaringType, prop.Name)
        c.Setter = LiteralProperty.CreateSetDelegate(prop.DeclaringType, prop.Name)

        Return c
    End Function

    Friend Sub New()
    End Sub

    ''' <summary>
    ''' リテラル名構築イベントを発生します
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Friend Sub RaiseLiteralNameBuildingEvent(e As LiteralNameBuildingEventArgs)
        RaiseEvent OnLiteralNameBuiding(Me, e)
    End Sub

    ''' <summary>
    ''' プロパティ情報
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Friend Property PropertyInfo As PropertyInfo

    Private _literalName As String
    ''' <summary>
    ''' リテラル名
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property LiteralName As String
        Get
            Return _literalName
        End Get
        Friend Set(value As String)
            _literalName = value
        End Set
    End Property

    Private _systemName As String
    ''' <summary>
    ''' システム名
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks>
    ''' 加工処理をしていないリテラル名
    ''' </remarks>
    Public Property SystemName As String
        Get
            Return _systemName
        End Get
        Protected Set(value As String)
            _systemName = value
        End Set
    End Property

    ''' <summary>
    ''' リテラルプロパティタイプを取得します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overridable ReadOnly Property LiteralPropertyType As Type
        Get
            Return Me.PropertyType
        End Get
    End Property

    ''' <summary>
    ''' リテラル値を取得します
    ''' </summary>
    ''' <param name="instance"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overridable Function GetLiteralValue(instance As Object) As Object
        Return Me.GetValue(instance, Nothing)
    End Function

    ''' <summary>
    ''' 追加用のリテラル値を取得します
    ''' </summary>
    ''' <param name="instance"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetLiteralValueForInsert(instance As Object) As Object
        If Me.HasSystemValue Then
            Me.SystemValueAttribute.RaiseSaveValueReadingEvent(instance, Me, Nothing)
        End If

        Return Me.GetLiteralValue(instance)
    End Function

    ''' <summary>
    ''' リテラル値を代入します
    ''' </summary>
    ''' <param name="instance"></param>
    ''' <param name="value"></param>
    ''' <remarks></remarks>
    Public Overridable Sub SetLiteralValue(instance As Object, value As Object)
        If instance Is Nothing Then Throw New ArgumentNullException("instance")

        'NULL処理
        If IsDBNull(value) OrElse value Is Nothing Then
            Me.SetValue(instance, Nothing, Nothing)
            Return
        End If

        'プロパティの型チェック
        Dim t As Type = Me.GetActualPropertyType
        Me.SetValue(instance, Convert.ChangeType(value, t), Nothing)

        '数値型の場合、型変換
        'If value.GetType.Equals(t) = False AndAlso IsNumeric(value) Then
        '    If t.Equals(GetType(Boolean)) AndAlso (value.ToString = "0" OrElse value.ToString = "1") Then
        '        value = If(value.ToString = "0", False, True)
        '    Else
        '        Select Case t
        '            Case GetType(Integer) : value = Integer.Parse(value.ToString)
        '            Case GetType(Decimal) : value = Decimal.Parse(value.ToString)
        '            Case GetType(Long) : value = Long.Parse(value.ToString)
        '            Case GetType(Double) : value = Double.Parse(value.ToString)
        '            Case GetType(SByte) : value = SByte.Parse(value.ToString)
        '            Case GetType(Short) : value = Short.Parse(value.ToString)
        '        End Select
        '    End If
        'End If

        'Me.SetValue(instance, value, Nothing)
    End Sub

    ''' <summary>
    ''' システム値属性を持っているかどうかを示します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property HasSystemValue As Boolean
        Get
            Return If(Me.SystemValueAttribute Is Nothing, False, True)
        End Get
    End Property

    Private _systemValueAttribute As SpecialValueAttribute
    ''' <summary>
    ''' システム値属性を取得します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property SystemValueAttribute As SpecialValueAttribute
        Get
            Return _systemValueAttribute
        End Get
        Friend Set(value As SpecialValueAttribute)
            _systemValueAttribute = value
        End Set
    End Property

#Region "PropertyInfo Inherits"
    ''' <summary>
    ''' このプロパティの属性を取得します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides ReadOnly Property Attributes As System.Reflection.PropertyAttributes
        Get
            Return Me.PropertyInfo.Attributes
        End Get
    End Property

    ''' <summary>
    ''' プロパティを読み取ることができるかどうかを示す値を取得します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides ReadOnly Property CanRead As Boolean
        Get
            Return Me.PropertyInfo.CanRead
        End Get
    End Property

    ''' <summary>
    ''' プロパティに書き込むことができるかどうかを示す値を取得します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides ReadOnly Property CanWrite As Boolean
        Get
            Return Me.PropertyInfo.CanWrite
        End Get
    End Property

    ''' <summary>
    ''' このメンバーを宣言するクラスを取得します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides ReadOnly Property DeclaringType As System.Type
        Get
            Return Me.PropertyInfo.DeclaringType
        End Get
    End Property

    ''' <summary>
    ''' このプロパティの get アクセサーと set アクセサーの配列を返します
    ''' </summary>
    ''' <param name="nonPublic"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overloads Overrides Function GetAccessors(nonPublic As Boolean) As System.Reflection.MethodInfo()
        Return Me.PropertyInfo.GetAccessors(nonPublic)
    End Function

    ''' <summary>
    ''' 派生クラスでオーバーライドされた場合、このメンバーに適用されているカスタム属性を返します
    ''' </summary>
    ''' <param name="inherit"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overloads Overrides Function GetCustomAttributes(inherit As Boolean) As Object()
        Return Me.PropertyInfo.GetCustomAttributes(inherit)
    End Function

    ''' <summary>
    ''' 派生クラスでオーバーライドされた場合、このメンバーに適用されているカスタム属性を返します
    ''' </summary>
    ''' <param name="attributeType"></param>
    ''' <param name="inherit"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overloads Overrides Function GetCustomAttributes(attributeType As System.Type, inherit As Boolean) As Object()
        Return Me.PropertyInfo.GetCustomAttributes(attributeType, inherit)
    End Function

    ''' <summary>
    ''' このプロパティの get アクセサーを表す MethodInfo を返します
    ''' </summary>
    ''' <param name="nonPublic"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overloads Overrides Function GetGetMethod(nonPublic As Boolean) As System.Reflection.MethodInfo
        Return Me.PropertyInfo.GetGetMethod(nonPublic)
    End Function

    ''' <summary>
    ''' 派生クラスによってオーバーライドされた場合に、プロパティのすべてのインデックス パラメーターの配列を返します
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function GetIndexParameters() As System.Reflection.ParameterInfo()
        Return Me.PropertyInfo.GetIndexParameters
    End Function

    ''' <summary>
    ''' このプロパティの set アクセサーを表す MethodInfo を返します
    ''' </summary>
    ''' <param name="nonPublic"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overloads Overrides Function GetSetMethod(nonPublic As Boolean) As System.Reflection.MethodInfo
        Return Me.PropertyInfo.GetSetMethod(nonPublic)
    End Function

    Protected Property Getter As Func(Of Object, Object)
    ''' <summary>
    ''' プロパティの値を返します
    ''' </summary>
    ''' <param name="obj"></param>
    ''' <param name="invokeAttr"></param>
    ''' <param name="binder"></param>
    ''' <param name="index"></param>
    ''' <param name="culture"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overloads Overrides Function GetValue(obj As Object, invokeAttr As System.Reflection.BindingFlags, binder As System.Reflection.Binder, index() As Object, culture As System.Globalization.CultureInfo) As Object
        Return Me.Getter.Invoke(obj)
    End Function

    Public Overrides Function GetValue(obj As Object, index() As Object) As Object
        Return Me.Getter.Invoke(obj)
    End Function

    ''' <summary>
    ''' 派生クラスでオーバーライドされた場合、このメンバーに、指定された型の属性またはその派生型の属性が 1 つ以上適用されているかどうかを示します
    ''' </summary>
    ''' <param name="attributeType"></param>
    ''' <param name="inherit"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function IsDefined(attributeType As System.Type, inherit As Boolean) As Boolean
        Return Me.PropertyInfo.IsDefined(attributeType, inherit)
    End Function

    ''' <summary>
    ''' 現在のメンバーの名前を取得します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides ReadOnly Property Name As String
        Get
            Return Me.PropertyInfo.Name
        End Get
    End Property

    ''' <summary>
    ''' このプロパティの型を取得します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides ReadOnly Property PropertyType As System.Type
        Get
            Return Me.PropertyInfo.PropertyType
        End Get
    End Property

    ''' <summary>
    ''' MemberInfo  のこのインスタンスを取得するために使用したクラス オブジェクトを取得します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides ReadOnly Property ReflectedType As System.Type
        Get
            Return Me.PropertyInfo.ReflectedType
        End Get
    End Property

    Protected Property Setter As Action(Of Object, Object)
    ''' <summary>
    ''' 指定したオブジェクトのプロパティ値に、指定した値を設定します
    ''' </summary>
    ''' <param name="obj"></param>
    ''' <param name="value"></param>
    ''' <param name="invokeAttr"></param>
    ''' <param name="binder"></param>
    ''' <param name="index"></param>
    ''' <param name="culture"></param>
    ''' <remarks></remarks>
    Public Overloads Overrides Sub SetValue(obj As Object, value As Object, invokeAttr As System.Reflection.BindingFlags, binder As System.Reflection.Binder, index() As Object, culture As System.Globalization.CultureInfo)
        Me.Setter.Invoke(obj, value)
    End Sub

    ''' <summary>
    ''' 指定したオブジェクトのプロパティ値に、指定した値を設定します
    ''' </summary>
    ''' <param name="obj"></param>
    ''' <param name="value"></param>
    ''' <param name="index"></param>
    ''' <remarks></remarks>
    Public Overrides Sub SetValue(obj As Object, value As Object, index() As Object)
        Me.Setter.Invoke(obj, value)
    End Sub
#End Region

    ''' <summary>
    ''' 値取取得デリゲート
    ''' </summary>
    ''' <param name="modelType"></param>
    ''' <param name="memberName"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Shared Function CreateGetDelegate(modelType As Type, memberName As String) As Func(Of Object, Object)
        Dim target = Expression.Parameter(GetType(Object), "target")

        Dim member = Expression.PropertyOrField(Expression.Convert(target, modelType), memberName)
        Dim value = Expression.Convert(member, GetType(Object))

        Dim lambda = Expression.Lambda(Of Func(Of Object, Object))(value, target)

        Return lambda.Compile()
    End Function

    ''' <summary>
    ''' 値保存デリゲート
    ''' </summary>
    ''' <param name="modelType"></param>
    ''' <param name="memberName"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Shared Function CreateSetDelegate(modelType As Type, memberName As String) As Action(Of Object, Object)
        Dim target = Expression.Parameter(GetType(Object), "target")
        Dim value = Expression.Parameter(GetType(Object), "value")

        Dim left = Expression.PropertyOrField(Expression.Convert(target, modelType), memberName)
        Dim right = Expression.Convert(value, left.Type)

        Dim lambda = Expression.Lambda(Of Action(Of Object, Object))(Expression.Assign(left, right), target, value)

        Return lambda.Compile()
    End Function

End Class